<html lang="en-US">

<head>
    
<title>「连载二」Gin搭建Blog API&#39;s （一） - 花落雨忧</title>

<meta property="og:title" content="「连载二」Gin搭建Blog API&#39;s （一） - 花落雨忧">



    



    
    <meta property="description" content="首先，在一个初始项目开始前，大家都要思考一下
[&amp;hellip;] 程序的文本配置写在代码中，好吗？
[&amp;hellip;] API 的错误码硬编码在程序中，合适吗？
[&amp;hellip;] db 句柄谁都去Open，没有统一管理，好吗？
[&amp;hellip;] 获取分页等公共参数，谁都自己写一套逻辑，好吗？
[&amp;hellip;] 显然在较正规的项目中，这些问题的答案都是不可以，为了解决这些问题，我们挑 &amp;hellip;">
    <meta property="og:description" content="首先，在一个初始项目开始前，大家都要思考一下
[&amp;hellip;] 程序的文本配置写在代码中，好吗？
[&amp;hellip;] API 的错误码硬编码在程序中，合适吗？
[&amp;hellip;] db 句柄谁都去Open，没有统一管理，好吗？
[&amp;hellip;] 获取分页等公共参数，谁都自己写一套逻辑，好吗？
[&amp;hellip;] 显然在较正规的项目中，这些问题的答案都是不可以，为了解决这些问题，我们挑 &amp;hellip;">
    






<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1">

<link rel="shortcut icon" href="https://www.lican.site/logo/logo.png" type="image/x-icon" />



<link rel="stylesheet" href="/css/style.min.css" />

<link rel="stylesheet" href="/css/reset.min.css" />




<script src="https://www.lican.site/js/highlight.min.js"></script>

<script>
hljs.configure({ ignoreUnescapedHTML: true })
hljs.highlightAll();
</script>


<script src="https://www.lican.site/js/jquery.min.js"></script>




<link href="https://www.lican.site/css/hugo-code.min.css" rel="stylesheet" />



    <style>
        .post-content img {
            max-width: 400px;
        }
    </style>
</head>

<body id="period" class="home blog">
    <a class="skip-content" href="#main">Press "Enter" to skip to content</a>
    <div id="overflow-container" class="overflow-container">
        <header class="site-header" id="site-header" role="banner">
    <div class="max-width">
        <div id="title-container" class="title-container">
            <div id="site-title" class="site-title"><a href="/">花落雨忧</a></div>
            <p class="tagline">思所及 力所行 方为真.</p>
        </div>
        
        <div id="menu-primary-container" class="menu-primary-container">
            <div class="icon-container">
            </div>
            <div id="menu-primary" class="menu-container menu-primary" role="navigation">
                <nav class="menu">
                    <ul id="menu-primary-items" class="menu-primary-items">
                        
                        
                        <li id="menu-item-0"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/posts" aria-current="page" tabindex="0">首页</a></li>
                        
                        <li id="menu-item-1"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/tech/" aria-current="page" tabindex="1">技术文档</a></li>
                        
                        <li id="menu-item-2"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/article/" aria-current="page" tabindex="2">文章</a></li>
                        
                        <li id="menu-item-3"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/project/" aria-current="page" tabindex="3">项目</a></li>
                        
                        <li id="menu-item-4"
                            class="menu-item menu-item-type-custom menu-item-object-custom ">
                            <a href="/about/" aria-current="page" tabindex="4">关于</a></li>
                        
                    </ul>
                </nav>
            </div>
        </div>
    </div>
</header>
        <div id="primary-container" class="primary-container">
            <div class="max-width">
                <section id="main" class="main" role="main">
                    <div id="loop-container" class="loop-container">
                        <div
                            class="post type-post status-publish format-standard hentry entry">
                            <article>
                                <div class="post-container">
                                    <div class="post-header">
                                        <h2 class="post-title">
                                            <a href="/posts/go/gin/2018-02-11-api-01/">「连载二」Gin搭建Blog API&#39;s （一）</a>
                                        </h2>
                                        
                                        <div class="post-byline">Published on
                                            <a class="date" href="javascript:;">2018/02/11</a>
                                            
                                            
                                            
                                            By 
                                            <a class="author" href="javascript:;">煎鱼</a></div>
                                            
                                        
                                    </div>
                                    <div class="post-content">
                                        <h2 id="思考">思考</h2>
<p>首先，在一个初始项目开始前，大家都要思考一下</p>
<ul>
<li>
<p>程序的文本配置写在代码中，好吗？</p>
</li>
<li>
<p>API 的错误码硬编码在程序中，合适吗？</p>
</li>
<li>
<p>db 句柄谁都去<code>Open</code>，没有统一管理，好吗？</p>
</li>
<li>
<p>获取分页等公共参数，谁都自己写一套逻辑，好吗？</p>
</li>
</ul>
<p>显然在较正规的项目中，这些问题的答案都是<strong>不可以</strong>，为了解决这些问题，我们挑选一款读写配置文件的库，目前比较火的有 <a href="https://github.com/spf13/viper">viper</a>，有兴趣你未来可以简单了解一下，没兴趣的话等以后接触到再说。</p>
<p>但是本系列选用 <a href="https://github.com/go-ini/ini">go-ini/ini</a> ，它的 <a href="https://ini.unknwon.io/">中文文档</a>。大家是必须需要要简单阅读它的文档，再接着完成后面的内容。</p>
<h2 id="本文目标">本文目标</h2>
<ul>
<li>编写一个简单的 API 错误码包。</li>
<li>完成一个 Demo 示例。</li>
<li>讲解 Demo 所涉及的知识点。</li>
</ul>
<h2 id="介绍和初始化项目">介绍和初始化项目</h2>
<h3 id="初始化项目目录">初始化项目目录</h3>
<p>在前一章节中，我们初始化了一个 <code>go-gin-example</code> 项目，接下来我们需要继续新增如下目录结构：</p>
<pre><code>go-gin-example/
├── conf
├── middleware
├── models
├── pkg
├── routers
└── runtime
</code></pre><ul>
<li>conf：用于存储配置文件</li>
<li>middleware：应用中间件</li>
<li>models：应用数据库模型</li>
<li>pkg：第三方包</li>
<li>routers 路由逻辑处理</li>
<li>runtime：应用运行时数据</li>
</ul>
<h3 id="添加-go-modules-replace">添加 Go Modules Replace</h3>
<p>打开 <code>go.mod</code> 文件，新增 <code>replace</code> 配置项，如下：</p>
<pre><code>module github.com/EDDYCJY/go-gin-example

go 1.13

require (...)

replace (
		github.com/EDDYCJY/go-gin-example/pkg/setting =&gt; ~/go-application/go-gin-example/pkg/setting
		github.com/EDDYCJY/go-gin-example/conf    	  =&gt; ~/go-application/go-gin-example/pkg/conf
		github.com/EDDYCJY/go-gin-example/middleware  =&gt; ~/go-application/go-gin-example/middleware
		github.com/EDDYCJY/go-gin-example/models 	  =&gt; ~/go-application/go-gin-example/models
		github.com/EDDYCJY/go-gin-example/routers 	  =&gt; ~/go-application/go-gin-example/routers
)
</code></pre><p>可能你会不理解为什么要特意跑来加 <code>replace</code> 配置项，首先你要看到我们使用的是完整的外部模块引用路径（<code>github.com/EDDYCJY/go-gin-example/xxx</code>），而这个模块还没推送到远程，是没有办法下载下来的，因此需要用 <code>replace</code> 将其指定读取本地的模块路径，这样子就可以解决本地模块读取的问题。</p>
<p><strong>注：后续每新增一个本地应用目录，你都需要主动去 go.mod 文件里新增一条 replace（我不会提醒你），如果你漏了，那么编译时会出现报错，找不到那个模块。</strong></p>
<h3 id="初始项目数据库">初始项目数据库</h3>
<p>新建 <code>blog</code> 数据库，编码为<code>utf8_general_ci</code>，在 <code>blog</code> 数据库下，新建以下表</p>
<p><strong>1、 标签表</strong></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sql" data-lang="sql"><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">TABLE</span> <span style="color:#f92672">`</span>blog_tag<span style="color:#f92672">`</span> (
  <span style="color:#f92672">`</span>id<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">10</span>) unsigned <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> AUTO_INCREMENT,
  <span style="color:#f92672">`</span>name<span style="color:#f92672">`</span> varchar(<span style="color:#ae81ff">100</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;标签名称&#39;</span>,
  <span style="color:#f92672">`</span>created_on<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">10</span>) unsigned <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;0&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;创建时间&#39;</span>,
  <span style="color:#f92672">`</span>created_by<span style="color:#f92672">`</span> varchar(<span style="color:#ae81ff">100</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;创建人&#39;</span>,
  <span style="color:#f92672">`</span>modified_on<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">10</span>) unsigned <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;0&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;修改时间&#39;</span>,
  <span style="color:#f92672">`</span>modified_by<span style="color:#f92672">`</span> varchar(<span style="color:#ae81ff">100</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;修改人&#39;</span>,
  <span style="color:#f92672">`</span>deleted_on<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">10</span>) unsigned <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;0&#39;</span>,
  <span style="color:#f92672">`</span><span style="color:#66d9ef">state</span><span style="color:#f92672">`</span> tinyint(<span style="color:#ae81ff">3</span>) unsigned <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;1&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;状态 0为禁用、1为启用&#39;</span>,
  <span style="color:#66d9ef">PRIMARY</span> <span style="color:#66d9ef">KEY</span> (<span style="color:#f92672">`</span>id<span style="color:#f92672">`</span>)
) ENGINE<span style="color:#f92672">=</span>InnoDB <span style="color:#66d9ef">DEFAULT</span> CHARSET<span style="color:#f92672">=</span>utf8 <span style="color:#66d9ef">COMMENT</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;文章标签管理&#39;</span>;
</code></pre></div><p><strong>2、 文章表</strong></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sql" data-lang="sql"><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">TABLE</span> <span style="color:#f92672">`</span>blog_article<span style="color:#f92672">`</span> (
  <span style="color:#f92672">`</span>id<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">10</span>) unsigned <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> AUTO_INCREMENT,
  <span style="color:#f92672">`</span>tag_id<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">10</span>) unsigned <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;0&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;标签ID&#39;</span>,
  <span style="color:#f92672">`</span>title<span style="color:#f92672">`</span> varchar(<span style="color:#ae81ff">100</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;文章标题&#39;</span>,
  <span style="color:#f92672">`</span><span style="color:#66d9ef">desc</span><span style="color:#f92672">`</span> varchar(<span style="color:#ae81ff">255</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;简述&#39;</span>,
  <span style="color:#f92672">`</span>content<span style="color:#f92672">`</span> text,
  <span style="color:#f92672">`</span>created_on<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">11</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#66d9ef">NULL</span>,
  <span style="color:#f92672">`</span>created_by<span style="color:#f92672">`</span> varchar(<span style="color:#ae81ff">100</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;创建人&#39;</span>,
  <span style="color:#f92672">`</span>modified_on<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">10</span>) unsigned <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;0&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;修改时间&#39;</span>,
  <span style="color:#f92672">`</span>modified_by<span style="color:#f92672">`</span> varchar(<span style="color:#ae81ff">255</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;修改人&#39;</span>,
  <span style="color:#f92672">`</span>deleted_on<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">10</span>) unsigned <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;0&#39;</span>,
  <span style="color:#f92672">`</span><span style="color:#66d9ef">state</span><span style="color:#f92672">`</span> tinyint(<span style="color:#ae81ff">3</span>) unsigned <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;1&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;状态 0为禁用1为启用&#39;</span>,
  <span style="color:#66d9ef">PRIMARY</span> <span style="color:#66d9ef">KEY</span> (<span style="color:#f92672">`</span>id<span style="color:#f92672">`</span>)
) ENGINE<span style="color:#f92672">=</span>InnoDB <span style="color:#66d9ef">DEFAULT</span> CHARSET<span style="color:#f92672">=</span>utf8 <span style="color:#66d9ef">COMMENT</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#39;文章管理&#39;</span>;
</code></pre></div><p><strong>3、 认证表</strong></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-sql" data-lang="sql"><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">TABLE</span> <span style="color:#f92672">`</span>blog_auth<span style="color:#f92672">`</span> (
  <span style="color:#f92672">`</span>id<span style="color:#f92672">`</span> int(<span style="color:#ae81ff">10</span>) unsigned <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> AUTO_INCREMENT,
  <span style="color:#f92672">`</span>username<span style="color:#f92672">`</span> varchar(<span style="color:#ae81ff">50</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;账号&#39;</span>,
  <span style="color:#f92672">`</span>password<span style="color:#f92672">`</span> varchar(<span style="color:#ae81ff">50</span>) <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">&#39;&#39;</span> <span style="color:#66d9ef">COMMENT</span> <span style="color:#e6db74">&#39;密码&#39;</span>,
  <span style="color:#66d9ef">PRIMARY</span> <span style="color:#66d9ef">KEY</span> (<span style="color:#f92672">`</span>id<span style="color:#f92672">`</span>)
) ENGINE<span style="color:#f92672">=</span>InnoDB <span style="color:#66d9ef">DEFAULT</span> CHARSET<span style="color:#f92672">=</span>utf8;

<span style="color:#66d9ef">INSERT</span> <span style="color:#66d9ef">INTO</span> <span style="color:#f92672">`</span>blog<span style="color:#f92672">`</span>.<span style="color:#f92672">`</span>blog_auth<span style="color:#f92672">`</span> (<span style="color:#f92672">`</span>id<span style="color:#f92672">`</span>, <span style="color:#f92672">`</span>username<span style="color:#f92672">`</span>, <span style="color:#f92672">`</span>password<span style="color:#f92672">`</span>) <span style="color:#66d9ef">VALUES</span> (<span style="color:#66d9ef">null</span>, <span style="color:#e6db74">&#39;test&#39;</span>, <span style="color:#e6db74">&#39;test123456&#39;</span>);

</code></pre></div><h2 id="编写项目配置包">编写项目配置包</h2>
<p>在 <code>go-gin-example</code> 应用目录下，拉取 <code>go-ini/ini</code> 的依赖包，如下：</p>
<pre><code>$ go get -u github.com/go-ini/ini
go: finding github.com/go-ini/ini v1.48.0
go: downloading github.com/go-ini/ini v1.48.0
go: extracting github.com/go-ini/ini v1.48.0
</code></pre><p>接下来我们需要编写基础的应用配置文件，在 <code>go-gin-example</code> 的<code>conf</code>目录下新建<code>app.ini</code>文件，写入内容：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ini" data-lang="ini"><span style="color:#75715e">#debug or release</span>
<span style="color:#a6e22e">RUN_MODE</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">debug</span>

<span style="color:#66d9ef">[app]</span>
<span style="color:#a6e22e">PAGE_SIZE</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">10</span>
<span style="color:#a6e22e">JWT_SECRET</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">23347$040412</span>

<span style="color:#66d9ef">[server]</span>
<span style="color:#a6e22e">HTTP_PORT</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">8000</span>
<span style="color:#a6e22e">READ_TIMEOUT</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">60</span>
<span style="color:#a6e22e">WRITE_TIMEOUT</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">60</span>

<span style="color:#66d9ef">[database]</span>
<span style="color:#a6e22e">TYPE</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">mysql</span>
<span style="color:#a6e22e">USER</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">数据库账号</span>
<span style="color:#a6e22e">PASSWORD</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">数据库密码</span>
<span style="color:#75715e">#127.0.0.1:3306</span>
<span style="color:#a6e22e">HOST</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">数据库IP:数据库端口号</span>
<span style="color:#a6e22e">NAME</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">blog</span>
<span style="color:#a6e22e">TABLE_PREFIX</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">blog_</span>
</code></pre></div><p>建立调用配置的<code>setting</code>模块，在<code>go-gin-example</code>的<code>pkg</code>目录下新建<code>setting</code>目录（注意新增 replace 配置），新建 <code>setting.go</code> 文件，写入内容：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">setting</span>

<span style="color:#f92672">import</span> (
	<span style="color:#e6db74">&#34;log&#34;</span>
	<span style="color:#e6db74">&#34;time&#34;</span>

	<span style="color:#e6db74">&#34;github.com/go-ini/ini&#34;</span>
)

<span style="color:#66d9ef">var</span> (
	<span style="color:#a6e22e">Cfg</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">ini</span>.<span style="color:#a6e22e">File</span>

	<span style="color:#a6e22e">RunMode</span> <span style="color:#66d9ef">string</span>

	<span style="color:#a6e22e">HTTPPort</span> <span style="color:#66d9ef">int</span>
	<span style="color:#a6e22e">ReadTimeout</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>
	<span style="color:#a6e22e">WriteTimeout</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>

	<span style="color:#a6e22e">PageSize</span> <span style="color:#66d9ef">int</span>
	<span style="color:#a6e22e">JwtSecret</span> <span style="color:#66d9ef">string</span>
)

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">init</span>() {
	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>
	<span style="color:#a6e22e">Cfg</span>, <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">ini</span>.<span style="color:#a6e22e">Load</span>(<span style="color:#e6db74">&#34;conf/app.ini&#34;</span>)
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;Fail to parse &#39;conf/app.ini&#39;: %v&#34;</span>, <span style="color:#a6e22e">err</span>)
	}

	<span style="color:#a6e22e">LoadBase</span>()
	<span style="color:#a6e22e">LoadServer</span>()
	<span style="color:#a6e22e">LoadApp</span>()
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">LoadBase</span>() {
	<span style="color:#a6e22e">RunMode</span> = <span style="color:#a6e22e">Cfg</span>.<span style="color:#a6e22e">Section</span>(<span style="color:#e6db74">&#34;&#34;</span>).<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;RUN_MODE&#34;</span>).<span style="color:#a6e22e">MustString</span>(<span style="color:#e6db74">&#34;debug&#34;</span>)
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">LoadServer</span>() {
	<span style="color:#a6e22e">sec</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">Cfg</span>.<span style="color:#a6e22e">GetSection</span>(<span style="color:#e6db74">&#34;server&#34;</span>)
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;Fail to get section &#39;server&#39;: %v&#34;</span>, <span style="color:#a6e22e">err</span>)
	}

	<span style="color:#a6e22e">HTTPPort</span> = <span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;HTTP_PORT&#34;</span>).<span style="color:#a6e22e">MustInt</span>(<span style="color:#ae81ff">8000</span>)
	<span style="color:#a6e22e">ReadTimeout</span> = <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>(<span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;READ_TIMEOUT&#34;</span>).<span style="color:#a6e22e">MustInt</span>(<span style="color:#ae81ff">60</span>)) <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>
	<span style="color:#a6e22e">WriteTimeout</span> =  <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>(<span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;WRITE_TIMEOUT&#34;</span>).<span style="color:#a6e22e">MustInt</span>(<span style="color:#ae81ff">60</span>)) <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">LoadApp</span>() {
	<span style="color:#a6e22e">sec</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">Cfg</span>.<span style="color:#a6e22e">GetSection</span>(<span style="color:#e6db74">&#34;app&#34;</span>)
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;Fail to get section &#39;app&#39;: %v&#34;</span>, <span style="color:#a6e22e">err</span>)
	}

	<span style="color:#a6e22e">JwtSecret</span> = <span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;JWT_SECRET&#34;</span>).<span style="color:#a6e22e">MustString</span>(<span style="color:#e6db74">&#34;!@)*#)!@U#@*!@!)&#34;</span>)
	<span style="color:#a6e22e">PageSize</span> = <span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;PAGE_SIZE&#34;</span>).<span style="color:#a6e22e">MustInt</span>(<span style="color:#ae81ff">10</span>)
}
</code></pre></div><p>当前的目录结构：</p>
<pre><code>go-gin-example
├── conf
│   └── app.ini
├── go.mod
├── go.sum
├── middleware
├── models
├── pkg
│   └── setting.go
├── routers
└── runtime
</code></pre><h2 id="编写-api-错误码包">编写 API 错误码包</h2>
<p>建立错误码的<code>e</code>模块，在<code>go-gin-example</code>的<code>pkg</code>目录下新建<code>e</code>目录（注意新增 replace 配置），新建<code>code.go</code>和<code>msg.go</code>文件，写入内容：</p>
<p><strong>1、 code.go：</strong></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">e</span>

<span style="color:#66d9ef">const</span> (
	<span style="color:#a6e22e">SUCCESS</span> = <span style="color:#ae81ff">200</span>
	<span style="color:#a6e22e">ERROR</span> = <span style="color:#ae81ff">500</span>
	<span style="color:#a6e22e">INVALID_PARAMS</span> = <span style="color:#ae81ff">400</span>

	<span style="color:#a6e22e">ERROR_EXIST_TAG</span> = <span style="color:#ae81ff">10001</span>
	<span style="color:#a6e22e">ERROR_NOT_EXIST_TAG</span> = <span style="color:#ae81ff">10002</span>
	<span style="color:#a6e22e">ERROR_NOT_EXIST_ARTICLE</span> = <span style="color:#ae81ff">10003</span>

	<span style="color:#a6e22e">ERROR_AUTH_CHECK_TOKEN_FAIL</span> = <span style="color:#ae81ff">20001</span>
	<span style="color:#a6e22e">ERROR_AUTH_CHECK_TOKEN_TIMEOUT</span> = <span style="color:#ae81ff">20002</span>
	<span style="color:#a6e22e">ERROR_AUTH_TOKEN</span> = <span style="color:#ae81ff">20003</span>
	<span style="color:#a6e22e">ERROR_AUTH</span> = <span style="color:#ae81ff">20004</span>
)
</code></pre></div><p><strong>2、 msg.go：</strong></p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">e</span>

<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">MsgFlags</span> = <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span> {
	<span style="color:#a6e22e">SUCCESS</span> : <span style="color:#e6db74">&#34;ok&#34;</span>,
	<span style="color:#a6e22e">ERROR</span> : <span style="color:#e6db74">&#34;fail&#34;</span>,
	<span style="color:#a6e22e">INVALID_PARAMS</span> : <span style="color:#e6db74">&#34;请求参数错误&#34;</span>,
	<span style="color:#a6e22e">ERROR_EXIST_TAG</span> : <span style="color:#e6db74">&#34;已存在该标签名称&#34;</span>,
	<span style="color:#a6e22e">ERROR_NOT_EXIST_TAG</span> : <span style="color:#e6db74">&#34;该标签不存在&#34;</span>,
	<span style="color:#a6e22e">ERROR_NOT_EXIST_ARTICLE</span> : <span style="color:#e6db74">&#34;该文章不存在&#34;</span>,
	<span style="color:#a6e22e">ERROR_AUTH_CHECK_TOKEN_FAIL</span> : <span style="color:#e6db74">&#34;Token鉴权失败&#34;</span>,
	<span style="color:#a6e22e">ERROR_AUTH_CHECK_TOKEN_TIMEOUT</span> : <span style="color:#e6db74">&#34;Token已超时&#34;</span>,
	<span style="color:#a6e22e">ERROR_AUTH_TOKEN</span> : <span style="color:#e6db74">&#34;Token生成失败&#34;</span>,
	<span style="color:#a6e22e">ERROR_AUTH</span> : <span style="color:#e6db74">&#34;Token错误&#34;</span>,
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetMsg</span>(<span style="color:#a6e22e">code</span> <span style="color:#66d9ef">int</span>) <span style="color:#66d9ef">string</span> {
	<span style="color:#a6e22e">msg</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">MsgFlags</span>[<span style="color:#a6e22e">code</span>]
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">ok</span> {
		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">msg</span>
	}

	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">MsgFlags</span>[<span style="color:#a6e22e">ERROR</span>]
}
</code></pre></div><h2 id="编写工具包">编写工具包</h2>
<p>在<code>go-gin-example</code>的<code>pkg</code>目录下新建<code>util</code>目录（注意新增 replace 配置），并拉取<code>com</code>的依赖包，如下：</p>
<pre><code>$ go get -u github.com/unknwon/com
</code></pre><h3 id="编写分页页码的获取方法">编写分页页码的获取方法</h3>
<p>在<code>util</code>目录下新建<code>pagination.go</code>，写入内容：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">util</span>

<span style="color:#f92672">import</span> (
	<span style="color:#e6db74">&#34;github.com/gin-gonic/gin&#34;</span>
	<span style="color:#e6db74">&#34;github.com/unknwon/com&#34;</span>

	<span style="color:#e6db74">&#34;github.com/EDDYCJY/go-gin-example/pkg/setting&#34;</span>
)

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">GetPage</span>(<span style="color:#a6e22e">c</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">Context</span>) <span style="color:#66d9ef">int</span> {
	<span style="color:#a6e22e">result</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>
	<span style="color:#a6e22e">page</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">com</span>.<span style="color:#a6e22e">StrTo</span>(<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">Query</span>(<span style="color:#e6db74">&#34;page&#34;</span>)).<span style="color:#a6e22e">Int</span>()
    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">page</span> &gt; <span style="color:#ae81ff">0</span> {
        <span style="color:#a6e22e">result</span> = (<span style="color:#a6e22e">page</span> <span style="color:#f92672">-</span> <span style="color:#ae81ff">1</span>) <span style="color:#f92672">*</span> <span style="color:#a6e22e">setting</span>.<span style="color:#a6e22e">PageSize</span>
    }

    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">result</span>
}
</code></pre></div><h2 id="编写-models-init">编写 models init</h2>
<p>拉取<code>gorm</code>的依赖包，如下：</p>
<pre><code>$ go get -u github.com/jinzhu/gorm
</code></pre><p>拉取<code>mysql</code>驱动的依赖包，如下：</p>
<pre><code>$ go get -u github.com/go-sql-driver/mysql
</code></pre><p>完成后，在<code>go-gin-example</code>的<code>models</code>目录下新建<code>models.go</code>，用于<code>models</code>的初始化使用</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">models</span>

<span style="color:#f92672">import</span> (
	<span style="color:#e6db74">&#34;log&#34;</span>
	<span style="color:#e6db74">&#34;fmt&#34;</span>

	<span style="color:#e6db74">&#34;github.com/jinzhu/gorm&#34;</span>
	<span style="color:#a6e22e">_</span> <span style="color:#e6db74">&#34;github.com/jinzhu/gorm/dialects/mysql&#34;</span>

	<span style="color:#e6db74">&#34;github.com/EDDYCJY/go-gin-example/pkg/setting&#34;</span>
)

<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">db</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">gorm</span>.<span style="color:#a6e22e">DB</span>

<span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Model</span> <span style="color:#66d9ef">struct</span> {
	<span style="color:#a6e22e">ID</span> <span style="color:#66d9ef">int</span> <span style="color:#e6db74">`gorm:&#34;primary_key&#34; json:&#34;id&#34;`</span>
	<span style="color:#a6e22e">CreatedOn</span> <span style="color:#66d9ef">int</span> <span style="color:#e6db74">`json:&#34;created_on&#34;`</span>
	<span style="color:#a6e22e">ModifiedOn</span> <span style="color:#66d9ef">int</span> <span style="color:#e6db74">`json:&#34;modified_on&#34;`</span>
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">init</span>() {
	<span style="color:#66d9ef">var</span> (
		<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>
		<span style="color:#a6e22e">dbType</span>, <span style="color:#a6e22e">dbName</span>, <span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">password</span>, <span style="color:#a6e22e">host</span>, <span style="color:#a6e22e">tablePrefix</span> <span style="color:#66d9ef">string</span>
	)

	<span style="color:#a6e22e">sec</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">setting</span>.<span style="color:#a6e22e">Cfg</span>.<span style="color:#a6e22e">GetSection</span>(<span style="color:#e6db74">&#34;database&#34;</span>)
	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#ae81ff">2</span>, <span style="color:#e6db74">&#34;Fail to get section &#39;database&#39;: %v&#34;</span>, <span style="color:#a6e22e">err</span>)
	}

	<span style="color:#a6e22e">dbType</span> = <span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;TYPE&#34;</span>).<span style="color:#a6e22e">String</span>()
	<span style="color:#a6e22e">dbName</span> = <span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;NAME&#34;</span>).<span style="color:#a6e22e">String</span>()
	<span style="color:#a6e22e">user</span> = <span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;USER&#34;</span>).<span style="color:#a6e22e">String</span>()
	<span style="color:#a6e22e">password</span> = <span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;PASSWORD&#34;</span>).<span style="color:#a6e22e">String</span>()
	<span style="color:#a6e22e">host</span> = <span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;HOST&#34;</span>).<span style="color:#a6e22e">String</span>()
	<span style="color:#a6e22e">tablePrefix</span> = <span style="color:#a6e22e">sec</span>.<span style="color:#a6e22e">Key</span>(<span style="color:#e6db74">&#34;TABLE_PREFIX&#34;</span>).<span style="color:#a6e22e">String</span>()

	<span style="color:#a6e22e">db</span>, <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">gorm</span>.<span style="color:#a6e22e">Open</span>(<span style="color:#a6e22e">dbType</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;%s:%s@tcp(%s)/%s?charset=utf8&amp;parseTime=True&amp;loc=Local&#34;</span>,
		<span style="color:#a6e22e">user</span>,
		<span style="color:#a6e22e">password</span>,
		<span style="color:#a6e22e">host</span>,
		<span style="color:#a6e22e">dbName</span>))

	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">err</span>)
	}

	<span style="color:#a6e22e">gorm</span>.<span style="color:#a6e22e">DefaultTableNameHandler</span> = <span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">db</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">gorm</span>.<span style="color:#a6e22e">DB</span>, <span style="color:#a6e22e">defaultTableName</span> <span style="color:#66d9ef">string</span>) <span style="color:#66d9ef">string</span>  {
	    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">tablePrefix</span> <span style="color:#f92672">+</span> <span style="color:#a6e22e">defaultTableName</span>;
	}

	<span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">SingularTable</span>(<span style="color:#66d9ef">true</span>)
	<span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">LogMode</span>(<span style="color:#66d9ef">true</span>)
	<span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">DB</span>().<span style="color:#a6e22e">SetMaxIdleConns</span>(<span style="color:#ae81ff">10</span>)
	<span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">DB</span>().<span style="color:#a6e22e">SetMaxOpenConns</span>(<span style="color:#ae81ff">100</span>)
}

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">CloseDB</span>() {
	<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">Close</span>()
}
</code></pre></div><h2 id="编写项目启动路由文件">编写项目启动、路由文件</h2>
<p>最基础的准备工作完成啦，让我们开始编写 Demo 吧！</p>
<h3 id="编写-demo">编写 Demo</h3>
<p>在<code>go-gin-example</code>下建立<code>main.go</code>作为启动文件（也就是<code>main</code>包），我们先写个<strong>Demo</strong>，帮助大家理解，写入文件内容：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>

<span style="color:#f92672">import</span> (
    <span style="color:#e6db74">&#34;fmt&#34;</span>
    <span style="color:#e6db74">&#34;net/http&#34;</span>

    <span style="color:#e6db74">&#34;github.com/gin-gonic/gin&#34;</span>

    <span style="color:#e6db74">&#34;github.com/EDDYCJY/go-gin-example/pkg/setting&#34;</span>
)

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
    <span style="color:#a6e22e">router</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">Default</span>()
    <span style="color:#a6e22e">router</span>.<span style="color:#a6e22e">GET</span>(<span style="color:#e6db74">&#34;/test&#34;</span>, <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">Context</span>) {
		<span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">JSON</span>(<span style="color:#ae81ff">200</span>, <span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">H</span>{
			<span style="color:#e6db74">&#34;message&#34;</span>: <span style="color:#e6db74">&#34;test&#34;</span>,
		})
	})

	<span style="color:#a6e22e">s</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Server</span>{
		<span style="color:#a6e22e">Addr</span>:           <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;:%d&#34;</span>, <span style="color:#a6e22e">setting</span>.<span style="color:#a6e22e">HTTPPort</span>),
		<span style="color:#a6e22e">Handler</span>:        <span style="color:#a6e22e">router</span>,
		<span style="color:#a6e22e">ReadTimeout</span>:    <span style="color:#a6e22e">setting</span>.<span style="color:#a6e22e">ReadTimeout</span>,
		<span style="color:#a6e22e">WriteTimeout</span>:   <span style="color:#a6e22e">setting</span>.<span style="color:#a6e22e">WriteTimeout</span>,
		<span style="color:#a6e22e">MaxHeaderBytes</span>: <span style="color:#ae81ff">1</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">20</span>,
	}

	<span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">ListenAndServe</span>()
}
</code></pre></div><p>执行<code>go run main.go</code>，查看命令行是否显示</p>
<pre><code>[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in &quot;debug&quot; mode. Switch to &quot;release&quot; mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /test                     --&gt; main.main.func1 (3 handlers)
</code></pre><p>在本机执行<code>curl 127.0.0.1:8000/test</code>，检查是否返回<code>{&quot;message&quot;:&quot;test&quot;}</code>。</p>
<h3 id="知识点">知识点</h3>
<p><strong>那么，我们来延伸一下 Demo 所涉及的知识点！</strong></p>
<h5 id="标准库">标准库</h5>
<ul>
<li><a href="https://golang.org/pkg/fmt/">fmt</a>：实现了类似 C 语言 printf 和 scanf 的格式化 I/O。格式化动作（&lsquo;verb&rsquo;）源自 C 语言但更简单</li>
<li><a href="https://golang.org/pkg/net/http/">net/http</a>：提供了 HTTP 客户端和服务端的实现</li>
</ul>
<h5 id="gin"><strong>Gin</strong></h5>
<ul>
<li><a href="https://gowalker.org/github.com/gin-gonic/gin#Default">gin.Default()</a>：返回 Gin 的<code>type Engine struct{...}</code>，里面包含<code>RouterGroup</code>，相当于创建一个路由<code>Handlers</code>，可以后期绑定各类的路由规则和函数、中间件等</li>
<li><a href="https://gowalker.org/github.com/gin-gonic/gin#IRoutes">router.GET(&hellip;){&hellip;}</a>：创建不同的 HTTP 方法绑定到<code>Handlers</code>中，也支持 POST、PUT、DELETE、PATCH、OPTIONS、HEAD 等常用的 Restful 方法</li>
<li><a href="https://gowalker.org/github.com/gin-gonic/gin#H">gin.H{&hellip;}</a>：就是一个<code>map[string]interface{}</code></li>
<li><a href="https://gowalker.org/github.com/gin-gonic/gin#Context">gin.Context</a>：<code>Context</code>是<code>gin</code>中的上下文，它允许我们在中间件之间传递变量、管理流、验证 JSON 请求、响应 JSON 请求等，在<code>gin</code>中包含大量<code>Context</code>的方法，例如我们常用的<code>DefaultQuery</code>、<code>Query</code>、<code>DefaultPostForm</code>、<code>PostForm</code>等等</li>
</ul>
<h5 id="httpserver-和-listenandserve">&amp;http.Server 和 ListenAndServe？</h5>
<p>1、http.Server：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Server</span> <span style="color:#66d9ef">struct</span> {
    <span style="color:#a6e22e">Addr</span>    <span style="color:#66d9ef">string</span>
    <span style="color:#a6e22e">Handler</span> <span style="color:#a6e22e">Handler</span>
    <span style="color:#a6e22e">TLSConfig</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">tls</span>.<span style="color:#a6e22e">Config</span>
    <span style="color:#a6e22e">ReadTimeout</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>
    <span style="color:#a6e22e">ReadHeaderTimeout</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>
    <span style="color:#a6e22e">WriteTimeout</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>
    <span style="color:#a6e22e">IdleTimeout</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>
    <span style="color:#a6e22e">MaxHeaderBytes</span> <span style="color:#66d9ef">int</span>
    <span style="color:#a6e22e">ConnState</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">net</span>.<span style="color:#a6e22e">Conn</span>, <span style="color:#a6e22e">ConnState</span>)
    <span style="color:#a6e22e">ErrorLog</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Logger</span>
}
</code></pre></div><ul>
<li>Addr：监听的 TCP 地址，格式为<code>:8000</code></li>
<li>Handler：http 句柄，实质为<code>ServeHTTP</code>，用于处理程序响应 HTTP 请求</li>
<li>TLSConfig：安全传输层协议（TLS）的配置</li>
<li>ReadTimeout：允许读取的最大时间</li>
<li>ReadHeaderTimeout：允许读取请求头的最大时间</li>
<li>WriteTimeout：允许写入的最大时间</li>
<li>IdleTimeout：等待的最大时间</li>
<li>MaxHeaderBytes：请求头的最大字节数</li>
<li>ConnState：指定一个可选的回调函数，当客户端连接发生变化时调用</li>
<li>ErrorLog：指定一个可选的日志记录器，用于接收程序的意外行为和底层系统错误；如果未设置或为<code>nil</code>则默认以日志包的标准日志记录器完成（也就是在控制台输出）</li>
</ul>
<p>2、 ListenAndServe：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">srv</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Server</span>) <span style="color:#a6e22e">ListenAndServe</span>() <span style="color:#66d9ef">error</span> {
    <span style="color:#a6e22e">addr</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">Addr</span>
    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">addr</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">&#34;&#34;</span> {
        <span style="color:#a6e22e">addr</span> = <span style="color:#e6db74">&#34;:http&#34;</span>
    }
    <span style="color:#a6e22e">ln</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">net</span>.<span style="color:#a6e22e">Listen</span>(<span style="color:#e6db74">&#34;tcp&#34;</span>, <span style="color:#a6e22e">addr</span>)
    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
    }
    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">Serve</span>(<span style="color:#a6e22e">tcpKeepAliveListener</span>{<span style="color:#a6e22e">ln</span>.(<span style="color:#f92672">*</span><span style="color:#a6e22e">net</span>.<span style="color:#a6e22e">TCPListener</span>)})
}
</code></pre></div><p>开始监听服务，监听 TCP 网络地址，Addr 和调用应用程序处理连接上的请求。</p>
<p>我们在源码中看到<code>Addr</code>是调用我们在<code>&amp;http.Server</code>中设置的参数，因此我们在设置时要用<code>&amp;</code>，我们要改变参数的值，因为我们<code>ListenAndServe</code>和其他一些方法需要用到<code>&amp;http.Server</code>中的参数，他们是相互影响的。</p>
<p>3、 <code>http.ListenAndServe</code>和 <a href="https://segmentfault.com/a/1190000013297625#articleHeader5">连载一</a> 的<code>r.Run()</code>有区别吗？</p>
<p>我们看看<code>r.Run</code>的实现：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">engine</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Engine</span>) <span style="color:#a6e22e">Run</span>(<span style="color:#a6e22e">addr</span> <span style="color:#f92672">...</span><span style="color:#66d9ef">string</span>) (<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) {
    <span style="color:#66d9ef">defer</span> <span style="color:#66d9ef">func</span>() { <span style="color:#a6e22e">debugPrintError</span>(<span style="color:#a6e22e">err</span>) }()

    <span style="color:#a6e22e">address</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">resolveAddress</span>(<span style="color:#a6e22e">addr</span>)
    <span style="color:#a6e22e">debugPrint</span>(<span style="color:#e6db74">&#34;Listening and serving HTTP on %s\n&#34;</span>, <span style="color:#a6e22e">address</span>)
    <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#a6e22e">address</span>, <span style="color:#a6e22e">engine</span>)
    <span style="color:#66d9ef">return</span>
}
</code></pre></div><p>通过分析源码，得知<strong>本质上没有区别</strong>，同时也得知了启动<code>gin</code>时的监听 debug 信息在这里输出。</p>
<p>4、 为什么 Demo 里会有<code>WARNING</code>？</p>
<p>首先我们可以看下<code>Default()</code>的实现</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#75715e">// Default returns an Engine instance with the Logger and Recovery middleware already attached.
</span><span style="color:#75715e"></span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Default</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">Engine</span> {
	<span style="color:#a6e22e">debugPrintWARNINGDefault</span>()
	<span style="color:#a6e22e">engine</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">New</span>()
	<span style="color:#a6e22e">engine</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">Logger</span>(), <span style="color:#a6e22e">Recovery</span>())
	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">engine</span>
}
</code></pre></div><p>大家可以看到默认情况下，已经附加了日志、恢复中间件的引擎实例。并且在开头调用了<code>debugPrintWARNINGDefault()</code>，而它的实现就是输出该行日志</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">debugPrintWARNINGDefault</span>() {
	<span style="color:#a6e22e">debugPrint</span>(<span style="color:#e6db74">`[WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
</span><span style="color:#e6db74">`</span>)
}
</code></pre></div><p>而另外一个<code>Running in &quot;debug&quot; mode. Switch to &quot;release&quot; mode in production.</code>，是运行模式原因，并不难理解，已在配置文件的管控下 :-)，运维人员随时就可以修改它的配置。</p>
<p>5、 Demo 的<code>router.GET</code>等路由规则可以不写在<code>main</code>包中吗？</p>
<p>我们发现<code>router.GET</code>等路由规则，在 Demo 中被编写在了<code>main</code>包中，感觉很奇怪，我们去抽离这部分逻辑！</p>
<p>在<code>go-gin-example</code>下<code>routers</code>目录新建<code>router.go</code>文件，写入内容：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">routers</span>

<span style="color:#f92672">import</span> (
    <span style="color:#e6db74">&#34;github.com/gin-gonic/gin&#34;</span>

    <span style="color:#e6db74">&#34;github.com/EDDYCJY/go-gin-example/pkg/setting&#34;</span>
)

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">InitRouter</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">Engine</span> {
    <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">New</span>()

    <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">Logger</span>())

    <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Use</span>(<span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">Recovery</span>())

    <span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">SetMode</span>(<span style="color:#a6e22e">setting</span>.<span style="color:#a6e22e">RunMode</span>)

    <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">GET</span>(<span style="color:#e6db74">&#34;/test&#34;</span>, <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">c</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">Context</span>) {
        <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">JSON</span>(<span style="color:#ae81ff">200</span>, <span style="color:#a6e22e">gin</span>.<span style="color:#a6e22e">H</span>{
            <span style="color:#e6db74">&#34;message&#34;</span>: <span style="color:#e6db74">&#34;test&#34;</span>,
        })
    })

    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">r</span>
}
</code></pre></div><p>修改<code>main.go</code>的文件内容：</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>

<span style="color:#f92672">import</span> (
	<span style="color:#e6db74">&#34;fmt&#34;</span>
	<span style="color:#e6db74">&#34;net/http&#34;</span>

	<span style="color:#e6db74">&#34;github.com/EDDYCJY/go-gin-example/routers&#34;</span>
	<span style="color:#e6db74">&#34;github.com/EDDYCJY/go-gin-example/pkg/setting&#34;</span>
)

<span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
	<span style="color:#a6e22e">router</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">routers</span>.<span style="color:#a6e22e">InitRouter</span>()

	<span style="color:#a6e22e">s</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Server</span>{
		<span style="color:#a6e22e">Addr</span>:           <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;:%d&#34;</span>, <span style="color:#a6e22e">setting</span>.<span style="color:#a6e22e">HTTPPort</span>),
		<span style="color:#a6e22e">Handler</span>:        <span style="color:#a6e22e">router</span>,
		<span style="color:#a6e22e">ReadTimeout</span>:    <span style="color:#a6e22e">setting</span>.<span style="color:#a6e22e">ReadTimeout</span>,
		<span style="color:#a6e22e">WriteTimeout</span>:   <span style="color:#a6e22e">setting</span>.<span style="color:#a6e22e">WriteTimeout</span>,
		<span style="color:#a6e22e">MaxHeaderBytes</span>: <span style="color:#ae81ff">1</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">20</span>,
	}

	<span style="color:#a6e22e">s</span>.<span style="color:#a6e22e">ListenAndServe</span>()
}
</code></pre></div><p>当前目录结构：</p>
<pre><code>go-gin-example/
├── conf
│   └── app.ini
├── main.go
├── middleware
├── models
│   └── models.go
├── pkg
│   ├── e
│   │   ├── code.go
│   │   └── msg.go
│   ├── setting
│   │   └── setting.go
│   └── util
│       └── pagination.go
├── routers
│   └── router.go
├── runtime
</code></pre><p>重启服务，执行 <code>curl 127.0.0.1:8000/test</code>查看是否正确返回。</p>
<p>下一节，我们将以我们的 Demo 为起点进行修改，开始编码！</p>
<h2 id="参考">参考</h2>
<h3 id="本系列示例代码">本系列示例代码</h3>
<ul>
<li><a href="https://github.com/EDDYCJY/go-gin-example">go-gin-example</a></li>
</ul>
<h2 id="关于">关于</h2>
<h3 id="修改记录">修改记录</h3>
<ul>
<li>第一版：2018 年 02 月 16 日发布文章</li>
<li>第二版：2019 年 10 月 01 日修改文章</li>
</ul>
<h2 id="heading">？</h2>
<p>如果有任何疑问或错误，欢迎在 <a href="https://github.com/EDDYCJY/blog">issues</a> 进行提问或给予修正意见，如果喜欢或对你有所帮助，欢迎 Star，对作者是一种鼓励和推进。</p>
<h3 id="我的公众号">我的公众号</h3>
<p><img src="https://image.eddycjy.com/8d0b0c3a11e74efd5fdfd7910257e70b.jpg" alt="image"></p>


                                        
                                        
                                        
                                        <div class="rp4wp-related-posts">
                                            <h3>相关文章</h3>
                                            <ul>
                                                
                                                <li>
                                                    <div class="rp4wp-related-post-content">
                                                        <a
                                                            href="https://www.lican.site/posts/go/gin/2018-02-10-install/">「连载一」Go 介绍与环境安装</a>
                                                    </div>
                                                </li>
                                                
                                            </ul>
                                        </div>
                                        
                                        
                                    </div>

                                    
                                    
                                    

                                    
                                    <div class="post-meta">
                                        
                                        
                                        <div class="post-tags">
                                            <ul>
                                            
                                            <li>
                                                <a href="/tags/go" title="View all posts tagged match">go</a>
                                            </li>
                                            
                                            <li>
                                                <a href="/tags/gin" title="View all posts tagged match">gin</a>
                                            </li>
                                            
                                            </ul>
                                        </div>
                                        
                                        
                                        <nav class="further-reading">
                                            
                                            <div class="previous">
                                                <span>&lt;&lt; Prev</span>
                                                <a href="https://www.lican.site/posts/go/gin/2018-02-10-install/"
                                                    rel="prev">「连载一」Go 介绍与环境安装</a> </div>
                                            
                                            
                                            <div class="next">
                                                <span>Next >></span>
                                                <a href="https://www.lican.site/posts/go/gin/2018-02-12-api-02/">「连载三」Gin搭建Blog API&#39;s （二）</a> 
                                            </div>
                                            
                                        </nav>
                                    </div>
                                    
                                    

                                    
                                    
                                    

                                </div>
                            </article>
                        </div>
                    </div>
                </section>
                <aside class="sidebar sidebar-primary" id="sidebar-primary" role="complementary">
    <h1 class="screen-reader-text">Sidebar</h1>
    

    
    
    <section id="text-2" class="widget widget_text">
        <div class="textwidget">
            
            <div id="profile">
                <div id="profile_picture"><img src="https://www.lican.site/logo/logo.png"></div>
                <div id="profile_intro">
                    <p><span class="name">Lican</span></p>
                    <p class="intro">全栈开发者，爱好造轮子。</p>
                </div>
            </div>
            
            <p>
                <script type="text/javascript">
                    (function ($) {
                        $(document).ready(function () {
                            var menuPrimaryContainer = $('#menu-primary-container');
                            var profile = $('#text-2');
                            $('#toggle-navigation').click(function () {
                                if (menuPrimaryContainer.hasClass('open')) {
                                    profile.removeClass('open');
                                } else {
                                    profile.addClass('open');
                                }
                            });
                        });
                    })(jQuery);
                </script>
            </p>
        </div>
    </section>
    
    
    
    
    
    <section id="text-5" class="widget widget_text">
        <h2 class="widget-title">开源项目</h2>
        <div class="textwidget">
            <div id="projects" style="line-height: 22px;">
                
                <a href="https://github.com/idoubi/gonews"
                    target="_blank">gonews</a>: &nbsp;Daily news for golang<br>
                
                <a href="https://github.com/idoubi/sql2struct"
                    target="_blank">sql2struct</a>: &nbsp;Generate go struct according to SQL<br>
                
                <a href="https://github.com/idoubi/goz"
                    target="_blank">goz</a>: &nbsp;Request library used in golang<br>
                
        </div>
    </section>
    
    

    
    
    
    
    

    
    
    

    
    
    

    
    
    
    
</aside>
            </div>
        </div>

        <footer id="site-footer" class="site-footer" role="contentinfo">
    <div class="max-width">
    </div>
    <div class="footer">
        <div id="footercontent">
            © lican.vip All rights reserved<br/>
            Built with Hugo Theme <a href="https://github.com/idoubi/hugo-theme-period" target="_blank">Period</a>
        </div>
    </div>
</footer>

<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?e8351b6d4626d5881d439ea1f6184baa";
      var s = document.getElementsByTagName("script")[0]; 
      s.parentNode.insertBefore(hm, s);
    })();
</script>
    
    
    </div>
    
</body>

</html>